/// Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using Microsoft.Win32;
using System.IO;
using Microsoft.VSPowerToys.Common.TaskManager;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Security.Permissions;
using Microsoft.VSPowerToys.PackInstallerDataModel;

namespace Microsoft.VSPowerToys.PackInstaller
{

    /// <summary>
    /// This is the main form for the application.  The user can get access to the toolbar, the Packs, and the tools on this window.
    /// </summary>
    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    [RegistryPermission(SecurityAction.Demand, Read = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall")]
    public partial class PackInstallerForm : Form
    {
        private Cursor oldCursor;
        private PackLibrary packData;
        private string oldSearchText = String.Empty;
        private string localFeedOverridePath;
        private Label packDownloadingWaitLabel;

        private TaskScheduler feedDownloadManager;

        public event EventHandler<EventArgs> OnPacksLoaded;
        public event EventHandler<EventArgs> OnToolsInstalled;

        /// <summary>
        /// Constructor
        /// </summary>
        public PackInstallerForm(string localFeedPath)
        {
            InitializeComponent();
            this.localFeedOverridePath = localFeedPath;
            this.Font = SystemFonts.DialogFont;
            this.packDownloadingWaitLabel = new Label();
            this.packDownloadingWaitLabel.Dock = DockStyle.Fill;
            this.packDownloadingWaitLabel.Text = Strings.DownloadingToolListMessage;
            this.packDownloadingWaitLabel.AutoSize = false;
            this.packDownloadingWaitLabel.TextAlign = ContentAlignment.MiddleCenter;
        }

        /// <summary>
        /// This method gets the latest feed downloaded, loads the data from the feed into the internal data objects, and loads the UI with
        /// the necessary information.
        /// </summary>
        public void RefreshDataAndGui()
        {
            oldCursor = this.Cursor;
            this.Cursor = Cursors.WaitCursor;
            packListPanel.Controls.Clear();
            packListPanel.Controls.Add(packDownloadingWaitLabel);
            this.Refresh();
            this.installSoftwareButton.Enabled = false;
            this.installationRadio.Checked = true;
            this.packInstallerFormToolTips.SetToolTip(packInstallerHomePageLink, ConfigurableStrings.PackInstallerHomePageURL);
            if (string.IsNullOrEmpty(localFeedOverridePath))
            {
                DownloadLatestPackFeed();
            }
            else
            {
                this.feedDownloadProgressBar.Visible = false;
                this.feedDownloadMessageLabel.Text = string.Empty;
                this.statusStrip1.Refresh();
                LoadPackFeed();
                this.Cursor = this.oldCursor;
            }
        }

        /// <summary>
        /// This method loads the feed file, and refreshes the GUI
        /// </summary>
        public void LoadPackFeed()
        {
            string currentFeedFile = string.Empty;

            // We want to make sure that if the user doesn't have the feed file in the download folder, we can still run if
            // there's one in the application start folder.  
            if (!string.IsNullOrEmpty(localFeedOverridePath))
            {
                if (File.Exists(localFeedOverridePath))
                {
                    currentFeedFile = localFeedOverridePath;
                }
            }
            else
            {
                if (File.Exists(Path.Combine(Application.CommonAppDataPath, ConfigurableStrings.PackFeedFileName)))
                {
                    currentFeedFile = Path.Combine(Application.CommonAppDataPath, ConfigurableStrings.PackFeedFileName);
                }
                else if (File.Exists(Path.Combine(Application.StartupPath, ConfigurableStrings.PackFeedFileName)))
                {
                    currentFeedFile = Path.Combine(Application.StartupPath, ConfigurableStrings.PackFeedFileName);
                }
            }

            // Do we have a feed file to load?
            if (!string.IsNullOrEmpty(currentFeedFile))
            {
                try
                {
                    // This call will initialize the internal data structures from the data set
                    this.packData = new PackLibrary();
                    this.packData.LoadPackDataFromAtomFeed(currentFeedFile);

                    // Make sure that we have the proper flags setup so the glyphs work properly...
                    CheckForInstalledSoftware();

                    // Create the UI for the packs and tools
                    this.DrawTagCloud();
                    LoadPacksToGUI();
                }
                catch (System.Security.SecurityException e)
                {
                    Program.ShowGlobalAwareMessageBox(this, Strings.SecurityExceptionCaption, e.Message);
                }
                catch (System.Xml.XmlException xmlException)
                {
                    // We had a bad XML file...
                    this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedLoadErrorMessage + xmlException.Message;
                }
                catch (System.Data.ConstraintException constraintException)
                {
                    // We had a bad XML file...
                    this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedLoadErrorMessage + constraintException.Message;
                }
                catch (InvalidOperationException e)
                {
                    // We had a bad XML file...
                    this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedLoadErrorMessage + e.Message;
                }
                catch (System.Xml.Schema.XmlSchemaValidationException e)
                {
                    // We had a bad XML file...
                    this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedLoadErrorMessage + e.Message;
                }
            }
            else
            {
                this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedFileNotFoundErrorMessage;
            }

            // This event gets raised primarily for testing purposes.  The automated tests need to know when everything
            // is all set before poking buttons.
            if (this.OnPacksLoaded != null)
            {
                this.OnPacksLoaded(this, new EventArgs());
            }
        }

        /// <summary>
        /// This method performs the actual downloading of the latest feed.
        /// </summary>
        private void DownloadLatestPackFeed()
        {
            this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedDownloadingStatusMessage;
            this.feedDownloadProgressBar.Value = 0;
            this.feedDownloadProgressBar.Visible = true;
            this.statusStrip1.Refresh();

            // We use the task manager class to download the XML feed file.
            feedDownloadManager = new TaskScheduler();
            feedDownloadManager.TaskCompleted += new EventHandler<TaskCompletedEventArgs>(feedDownloadManager_TaskCompleted);
            feedDownloadManager.QueueCompleted += new EventHandler(feedDownloadManager_QueueCompleted);
            string fileName = Path.Combine(Application.CommonAppDataPath, ConfigurableStrings.PackFeedFileName);
            DownloadTask task = new DownloadTask(new Uri(ConfigurableStrings.PackFeedURL), fileName);
            task.ProgressAvailable += new EventHandler<TaskProgressEventArgs>(DownloadTaskProgressAvailable);
            feedDownloadManager.EnqueueTask(task);
            feedDownloadManager.Start();
        }

        /// <summary>
        /// This method uses the product GUID provided in the tools definitions in the feed file to find out if each product is installed.
        /// We rely on the ARP data stored in the registry to be accurate for this to work.
        /// </summary>
        private void CheckForInstalledSoftware()
        {
            // Open the primary source key...
            RegistryKey uninstallKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
            if (uninstallKey != null && this.packData != null)
            {
                foreach (ToolItem currentTool in this.packData.Tools)
                {
                    if (String.IsNullOrEmpty(currentTool.ProductGuid))
                    {
                        currentTool.CurrentlyInstalled = false;
                        continue;
                    }
                    // Try and open the key that goes with this tool.
                    RegistryKey currentToolKey = uninstallKey.OpenSubKey("{" + currentTool.ProductGuid + "}");
                    if (currentToolKey != null)
                    {
                        // Get the Major Version value...
                        object majorObject = currentToolKey.GetValue("VersionMajor");

                        if (majorObject != null)
                        {
                            int installedMajorVersion = (int)majorObject;
                            // Parse out the version data we have from the feed...
                            string[] versionStrings = currentTool.VersionString.Split('.');

                            int feedMajorVersion = 0;

                            bool feedMajorParseWorked = int.TryParse(versionStrings[0], out feedMajorVersion);

                            if (feedMajorParseWorked)
                            {
                                // Do the major versions match?
                                if (installedMajorVersion == feedMajorVersion)
                                {
                                    // Get the Minor Version value...
                                    object minorObject = currentToolKey.GetValue("VersionMinor", 0);

                                    if (minorObject != null)
                                    {
                                        int installedMinorVersion = (int)minorObject;

                                        int feedMinorVersion = 0;
                                        bool feedMinorParseWorked = false;

                                        //Parse out the minor version from the feed data...
                                        if (versionStrings.Length > 1)
                                        {
                                            feedMinorParseWorked = int.TryParse(versionStrings[1], out feedMinorVersion);
                                        }

                                        if (feedMinorParseWorked)
                                        {
                                            // Do the minor versions match?
                                            if (installedMinorVersion == feedMinorVersion)
                                            {
                                                currentTool.CurrentlyInstalled = true;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        currentTool.CurrentlyInstalled = true;
                                    }
                                }
                            }
                        }
                        currentToolKey.Close();
                    }
                }
                uninstallKey.Close();
            }
        }

        /// <summary>
        /// Gathers tag information from the pack list and draws the tag cloud.
        /// </summary>
        private void DrawTagCloud()
        {
            if (this.packData == null) return;
            Collection<string> tags = new Collection<string>();
            foreach (ToolItem item in packData.Tools)
            {
                foreach (string tag in item.Tags)
                {
                    tags.Add(tag);
                }
            }
            this.tagAndFilterControl.BuildTagList(tags);
        }

        /// <summary>
        /// Filters the items being displayed on the panel
        /// </summary>
        /// <param name="calledBySearch">set to true if method is called because search keyword has been changed</param>
        private void FilterPacks(bool calledBySearch)
        {
            bool searchVisible = calledBySearch && this.tagAndFilterControl.SearchText.StartsWith(oldSearchText);
            if (calledBySearch) this.oldSearchText = this.tagAndFilterControl.SearchText;
            NativeMethods.SuspendPainting(this.packListPanel);
            int shownCount = 0;
            foreach (PackListControl packControl in packListPanel.Controls)
            {
                shownCount += packControl.FilterTools(this.tagAndFilterControl.SelectedTag, this.tagAndFilterControl.SearchText, searchVisible);
                packControl.SizePackAndTools();
            }
            this.tagAndFilterControl.DisplayedItemCount = shownCount;
            NativeMethods.ResumePainting(this.packListPanel);
            this.packListPanel.Refresh();
        }

        /// <summary>
        /// This method dynamically creates a PackListControl for each pack defined in the feed file and adds it to the GUI.
        /// Once created it passes the data from the feed into the PackListControl to be organized and displayed.
        /// </summary>
        private void LoadPacksToGUI()
        {
            if (this.packData == null) return;
            int totalCount = 0;
            int shownCount = 0;
            PackListControl packItem = null;
            NativeMethods.SuspendPainting(this.packListPanel);
            this.packListPanel.SuspendLayout();
            packListPanel.Controls.Clear();

            SortedList<string, PackItem> packList = this.packData.GetGroupsOfTools(this.tagAndFilterControl.SelectedSortMethod, this.tagAndFilterControl.SortOrder);

            for (int i = 0; i < packList.Count; i++)
            {
                totalCount += packList.Values[i].Tools.Count;
                packItem = new PackListControl();
                packItem.Name = packList.Values[i].Name;
                packItem.TabIndex = i;
                // Docking to the top keeps the controls nice and snug as well as letting the vertical autoscroll work properly.
                packItem.Dock = DockStyle.Top;
                this.packListPanel.Controls.Add(packItem);
                packItem.LoadPackData(packList.Values[i]);
                shownCount += packItem.FilterTools(this.tagAndFilterControl.SelectedTag, this.tagAndFilterControl.SearchText, false);
                packItem.BringToFront();
            }

            this.tagAndFilterControl.TotalItemCount = totalCount;
            this.tagAndFilterControl.DisplayedItemCount = shownCount;

            this.packListPanel.ResumeLayout(true);
            NativeMethods.ResumePainting(this.packListPanel);
        }

        /// <summary>
        /// This method updates the enabled status of the Install button.  The forceButtonOn parameter allows callers to specify
        /// that they already know that the button should be turned on.  Basically if one of the tools or packs gets checked we know
        /// the button should be enabled.  But if the pack or tool was UNchecked then we need to run through and find out whether the
        /// button should be on.
        /// </summary>
        /// <param name="forceButtonOn"></param>
        public void UpdateInstallButton(bool forceButtonOn)
        {
            bool installButtonEnabled = forceButtonOn;
            if (!forceButtonOn)
            {

                foreach (ToolItem tool in packData.Tools)
                {
                    if (tool.InstallTool)
                    {
                        installButtonEnabled = true;
                        break;
                    }
                }
            }

            this.installSoftwareButton.Enabled = installButtonEnabled;
        }

        /// <summary>
        /// Event handler for the Home Page link click, this will bring up the system registered webbrowser to the home page of the Power Toys.
        /// Or whatever URL is configured in the resource file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void packInstallerHomePageLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(Microsoft.VSPowerToys.PackInstaller.ConfigurableStrings.PackInstallerHomePageURL);
        }

        /// <summary>
        /// This method is the click event handler for the Exit button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void exitButton_Click(object sender, EventArgs e)
        {
            //TODO:  Check to make sure nothing has changed before closing
            this.Close();
        }

        /// <summary>
        /// This method is the click event handler for the Install / Update software button.
        /// Clicking the button will first display the license agreement, and if the user accepts it, kick off the install process.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void installSoftwareButton_Click(object sender, EventArgs e)
        {
            if (installationRadio.Checked)
            {
                // Bring up the download window for the license agreements.
                LicenseAgreementDownloadForm licenseDownloadWindow = new LicenseAgreementDownloadForm(this.packData);
                if (licenseDownloadWindow.ShowDialog(this) == DialogResult.OK)
                {
                    // If everything went okay with downloading the licenses then we can show the license agreement form to start the install process
                    MultipleLicenseAgreementForm licenseWindow = new MultipleLicenseAgreementForm();

                    // Pass the data over to the form...
                    licenseWindow.SetLicenseAgreementData(GenerateLicenseAgreementData());
                    if (licenseWindow.ShowDialog(this) == DialogResult.Yes)
                    {
                        // User agreed to the license so we can go ahead with the download and installation of the tools.
                        ProgressResultsForm progressWindow = new ProgressResultsForm(this.packData, this.maintenanceRadio.Checked);

                        // This sets up another testing event...
                        if (OnToolsInstalled != null)
                        {
                            progressWindow.OnToolsInstalled += OnToolsInstalled;
                        }

                        // Show the window to start the download process...
                        progressWindow.ShowDialog(this);
                    }
                }
            }
            else
            {
                // When we're uninstalling we don't need to actually show the license agreement, we can just go ahead with the uninstall.
                ProgressResultsForm progressWindow = new ProgressResultsForm(this.packData, this.maintenanceRadio.Checked);

                // This sets up another testing event...
                if (OnToolsInstalled != null)
                {
                    progressWindow.OnToolsInstalled += OnToolsInstalled;
                }

                // Show the window to start the uninstall process...
                progressWindow.ShowDialog(this);
            }
        }

        /// <summary>
        /// Click event for the Refresh List button.  This basically just performs the startup sequence of downloading the feed
        /// file and seeding the GUI.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void refreshListButton_Click(object sender, EventArgs e)
        {
            RefreshDataAndGui();
        }

        /// <summary>
        /// Event for when the task manager's queue is empty for downloading the feed file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void feedDownloadManager_QueueCompleted(object sender, EventArgs e)
        {
            if (!this.InvokeRequired)
            {
                ((TaskScheduler)sender).Stop(false);
            }
            else
            {
                this.BeginInvoke(new EventHandler(feedDownloadManager_QueueCompleted), sender, e);
            }
        }

        /// <summary>
        /// Event for when the feed file download task is completed.
        /// This kicks off the loading of the GUI with the pack data.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void feedDownloadManager_TaskCompleted(object sender, TaskCompletedEventArgs e)
        {
            if (!(sender is TaskScheduler))
            {
                throw new ArgumentException(PackInstaller.Strings.TaskManagerArgumentExceptionErrorMessage, "sender");
            }

            if (!this.InvokeRequired)
            {
                if (e.OwnerTask is DownloadTask)
                {
                    // The task of downloading the feed is complete, so no more need for the progress bar...
                    this.feedDownloadProgressBar.Visible = false;

                    // Tell the user what the outcome of the download was...
                    if (e.IsSuccess)
                    {
                        this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedDownloadSuccessStatusMessage;
                    }
                    else
                    {
                        this.feedDownloadMessageLabel.BackColor = Color.FromArgb(255, 255, 192);
                        this.feedDownloadMessageLabel.Text = PackInstaller.Strings.PackFeedDownloadFailureErrorMessage;
                    }
                    this.statusStrip1.Refresh();

                    // Load the pack data, this will either be the file we just downloaded or the last one we had if the download failed.
                    LoadPackFeed();
                    this.Cursor = oldCursor;
                }
            }
            else
            {
                this.BeginInvoke(new EventHandler<TaskCompletedEventArgs>(this.feedDownloadManager_TaskCompleted), sender, e);
            }
        }

        /// <summary>
        /// Event for updating the progress bar as the pack feed downloads.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="current"></param>
        /// <param name="max"></param>
        void DownloadTaskProgressAvailable(object sender, TaskProgressEventArgs e)
        {
            if (!this.InvokeRequired)
            {
                if ((e.MaximumProgressValue > 0) && (e.CurrentProgress <= e.MaximumProgressValue))
                {
                    if (this.feedDownloadProgressBar.Visible != true)
                    {
                        this.feedDownloadProgressBar.Visible = true;
                    }
                    // Calculate how much is done being downloaded...
                    int newVal = (int)((double)((double)e.CurrentProgress / (double)e.MaximumProgressValue) * 100);
                    this.feedDownloadProgressBar.Value = newVal;
                    this.statusStrip1.Refresh();
                }
                else
                {
                    this.feedDownloadProgressBar.Visible = false;
                    this.feedDownloadMessageLabel.Text = Strings.NoDownloadProgressAvailable;
                }
            }
            else
            {
                this.BeginInvoke(new EventHandler<TaskProgressEventArgs>(this.DownloadTaskProgressAvailable), sender, new TaskProgressEventArgs(e.CurrentTask, e.CurrentProgress, e.MaximumProgressValue));
            }
        }

        /// <summary>
        /// This method will pull the license data from the tools data and put it into the right form for the 
        /// license window.
        /// </summary>
        private ToolsLicenseList GenerateLicenseAgreementData()
        {
            ToolsLicenseList data = new ToolsLicenseList();
            DataTable licenseTable = data.Tables["LicenseEntry"];


            foreach (ToolItem tool in packData.Tools)
            {
                if (tool.InstallTool)
                {
                    DataRow newRow = licenseTable.NewRow();
                    newRow["ToolName"] = tool.ToolName;

                    // We append the license file name to the place we downloaded it to...
                    newRow["LicenseFileName"] = Path.Combine(Application.CommonAppDataPath, tool.LicenseAgreementFileName);
                    licenseTable.Rows.Add(newRow);
                }
            }
            return (data);

        }

        /// <summary>
        /// This event handler gets called when the radio button for Installation / Maintenance gets flipped...
        /// We just need to update the check boxes and change the label on the button...
        /// </summary>
        private void installationRadio_CheckedChanged(object sender, EventArgs e)
        {
            if (installationRadio.Checked)
            {
                RefreshPacksAndToolsInstallCheckboxes();
                this.installSoftwareButton.Text = Strings.ToolInstallButtonLabel;
                this.installSoftwareButton.Enabled = false;
            }
        }

        /// <summary>
        /// This event handler gets called when the radio button for Installation / Maintenance gets flipped...
        /// We just need to update the check boxes and change the label on the button...
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void maintenanceRadio_CheckedChanged(object sender, EventArgs e)
        {

            if (maintenanceRadio.Checked)
            {
                RefreshPacksAndToolsInstallCheckboxes();
                this.installSoftwareButton.Text = Strings.ToolUninstallButtonLabel;
                this.installSoftwareButton.Enabled = false;
            }
        }

        /// <summary>
        /// This method switches the GUI from install mode to maintenance mode...
        /// </summary>
        private void RefreshPacksAndToolsInstallCheckboxes()
        {
            Cursor oldCursor = this.Cursor;
            this.Cursor = Cursors.WaitCursor;
            this.packListPanel.Visible = false;
            this.packListPanel.Refresh();
            this.tagAndFilterControl.ResetTagAndFilter();

            for (int i = 0; i < this.packListPanel.Controls.Count; i++)
            {
                PackListControl packControl = this.packListPanel.Controls[i] as PackListControl;

                if (packControl != null)
                {
                    packControl.SetToolsMaintenanceMode(maintenanceRadio.Checked);
                }
            }

            // Make sure we disable the install / uninstall button since switching the maintenance mode
            // clears the install flags.
            this.installSoftwareButton.Enabled = false;
            this.packListPanel.Visible = true;
            this.packListPanel.Refresh();
            this.tagAndFilterControl.Enabled = installationRadio.Checked;
            this.Cursor = oldCursor;

        }

        /// <summary>
        /// This event handler gets called when the sorting method gets changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SortItems(object sender, SortMethodSelectedEventArgs e)
        {
            if (this.packData != null)
            {
                LoadPacksToGUI();
            }
        }

        /// <summary>
        /// This method detects the Visual Studio process and warns the user that its running.
        /// </summary>
        private static DialogResult DetectVisualStudioRunning()
        {
            DialogResult closeVisualStudio = DialogResult.Ignore;
            Process[] visualStudioProcesses = Process.GetProcessesByName("devenv");
            if (visualStudioProcesses.Length > 0)
            {
                VisualStudioDetectedForm vsdForm = new VisualStudioDetectedForm();
                closeVisualStudio = vsdForm.ShowDialog();
                if (closeVisualStudio == DialogResult.OK)
                {
                    // Shut down all instances of visual studio
                    foreach (Process proc in visualStudioProcesses)
                    {
                        proc.CloseMainWindow();
                    }
                }
            }
            return (closeVisualStudio);
        }

        /// <summary>
        /// This event handler is triggered when the form loads.
        /// Some initialization needs to be done here instead of in the constructor
        /// so we can be sure all the necessary data is ready.
        /// </summary>
        /// <param name="sender" />
        /// <param name="e" />
        private void PackInstallerForm_Load(object sender, EventArgs e)
        {
            DialogResult exitPackInstaller = DialogResult.None;

            if (!PackInstallerLicenseAgreementForm.HasCurrentLicenseVersionBeenAccepted())
            {
                PackInstallerLicenseAgreementForm licenseForm = new PackInstallerLicenseAgreementForm();
                exitPackInstaller = licenseForm.ShowDialog();
            }

            if (exitPackInstaller != DialogResult.Cancel)
            {
                exitPackInstaller = DetectVisualStudioRunning();
            }

            if ((exitPackInstaller == DialogResult.Cancel) || (exitPackInstaller == DialogResult.Abort))
            {
                this.Close();
                Application.Exit();
            }
            else
            {
                Dictionary<string, ToolItem.SortByField> sortFields = new Dictionary<string, ToolItem.SortByField>();
                sortFields.Add(Strings.SortFieldNameDefaultGroups, ToolItem.SortByField.DefaultGroup);
                sortFields.Add(Strings.SortFieldNameToolName, ToolItem.SortByField.ToolName);
                sortFields.Add(Strings.SortFieldNameAuthor, ToolItem.SortByField.Author);
                sortFields.Add(Strings.SortFieldNameReleaseDate, ToolItem.SortByField.ReleaseDate);
                sortFields.Add(Strings.SortFieldNameInstalled, ToolItem.SortByField.CurrentlyInstalled);

                this.tagAndFilterControl.UpdateSortList(sortFields);
                this.tagAndFilterControl.SetSortMethod(ToolItem.SortByField.DefaultGroup, SortOrder.Ascending);
            }
        }

        /// <summary>
        /// This event handler is triggered when the text in the search box in the filtering area is 
        /// changed.
        /// </summary>
        /// <param name="sender" />
        /// <param name="e" />
        private void tagAndFilterControl_SearchTextChanged(object sender, SearchTextChangedEventArgs e)
        {
            this.FilterPacks(true);
        }

        /// <summary>
        /// This event handler is triggered when the selected tag changes.
        /// </summary>
        /// <param name="sender" />
        /// <param name="e" />
        private void tagAndFilterControl_TagSelectionChanged(object sender, TagSelectedEventArgs e)
        {
            this.FilterPacks(false);
        }

        /// <summary>
        /// This event handler is triggered when the form is shown for the first time.
        /// When it is, we want to update the data and the controls created for the packs and tools.
        /// </summary>
        /// <param name="sender" />
        /// <param name="e" />
        private void PackInstallerForm_Shown(object sender, EventArgs e)
        {
            RefreshDataAndGui();
            if (this.packListPanel.Controls.Count > 0)
            {
                this.packListPanel.Controls[0].Select();
            }
        }

        /// <summary>
        /// This event handler is triggered when the panel that hosts the pack list resizes.
        /// We need to handle the event here so that the right controls get properly resized.
        /// If we try to handle the resizing only inside of the tool items, then the container controls
        /// don't get resized properly.
        /// </summary>
        /// <param name="sender" />
        /// <param name="e" />
        private void packListPanel_Resize(object sender, EventArgs e)
        {
            NativeMethods.SuspendPainting(packListPanel);
            packListPanel.SuspendLayout();
            PackListControl packControl = null;
            foreach (Control pack in packListPanel.Controls)
            {
                packControl = pack as PackListControl;
                if (packControl != null)
                {
                    packControl.SizePackAndTools();
                }
            }
            packListPanel.ResumeLayout();
            NativeMethods.ResumePainting(packListPanel);
        }

        /// <summary>
        /// This event handler is triggered when the form starts closing.
        /// We need to make sure our download threads get killed before the window goes away
        /// to prevent illegal access to references that have been disposed.
        /// </summary>
        /// <param name="sender" />
        /// <param name="e" />
        private void PackInstallerForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (feedDownloadManager != null)
            {
                Cursor oldCursor = this.Cursor;
                this.Cursor = Cursors.WaitCursor;
                feedDownloadManager.Stop(true);
                this.Cursor = oldCursor;
            }
        }
    }
}
